'''

函数：
函数是封装了特定功能的可重复使用的代码块，它让程序更清晰，避免重复，便于测试和维护
函数的好处：修改只需要修改一处
逻辑清晰，并且可读性强
可以被其他的模块导入并且重复的使用

函数四大类型：
1.内置函数：python自带  print  len   int    type
2.标准库函数： python的标准库  json     random   需要使用import 导入
3.第三方的函数   python社区开发的，需要我们 使用 pip  install 来下载之后再使用  import导入使用
4.自定义的函数  用户自己编写的   不需要导入
'''

# print("小兔子乖乖")
# print('把门打开')
# .........N  使代码膨胀

# def sing_rabbit_song():
#     print("小兔子乖乖")
#     print('把门打开')
# sing_rabbit_song()
# sing_rabbit_song()
# sing_rabbit_song()
# sing_rabbit_song()


#内置函数
# text = 'Hello'
# print(len(text))
# print(type(text))


#标准库函数
# import math
#
# print(math.pi)   # pi使表示 圆周率
# print(math.sqrt(25))   # sqrt  计算了25的平方根


#第三方的函数

# import  numpy as np  #做科学运算的库
# arr = np.zeros(3)
# print(arr)


#自定义的函数
#
# def greet(name):
#     return f"Hello,{name}!"   # 这个name 是一个形参，allen是实参
# print(greet('Allen'))
#
# """
# 社么是形参，什么是实参
# 形参：函数内部用来接收外部传入的变量名称
# 实参:传递给函数具体的值
# 函数的基本语法

# def 函数名称(参数的列表):
#     函数体
#     [reture   返回值]



# def say_hello():
#     print("hello")
# say_hello()

"""
扩展：函数是对象的一等公民
再python中，函数是function类型的对象
他可以，赋值给变量，作为参数进行传递，作为返回值，存放数据结构
"""

#赋值给变量

# def greet():  # 创建一个函数
#     return "Hi"    #命名返回值 为 Hi
# f = greet
# print(f())


# def call_twice(func):    # 是一个高阶的函数，接收另一个函数作为参数
#     func()  #第一次调用传入的函数
#     func()  #第二次调用传入的函数
#
# def hello():   # 0x000002764101DAF0 在内存中的一个地址，是一个十六进制
#     print("Hello")
#
# # call_twice(hello)   # 是使用了 call_twice 调用了 hello 来输出
# # print(type(hello))
# print(hello)


# def exector(function_to_run):
#     print("准备执行函数....")
#     function_to_run()
#     print("函数执行完毕！")
# def say_hello():
#     print('Hello!')
# def say_goodbye():
#     print('Goodbye')
# exector(say_hello)
# exector(say_goodbye)

"""
准备执行函数
Hello
函数执行完毕
准备执行函数
Goodbye
函数执行完毕

"""

#
# def executor(function_to_run):   #高阶函数，可以接收其他函数的值
#     print("准备执行函数")
#     function_to_run()
#     print("函数执行完毕")
# def say_hello():
#     print("Hello")
#
# executor(say_hello)


#参数
#位置参数：按照定义的顺序传入

# def introduce(name,age):
#     print(f"我叫{name},今年{age}岁。")
# # introduce("小明",12)  #可运行
# introduce(12,"小明")  #逻辑错误，但是可以运行


#关键字参数

# def introduce(name,age):
#     print(f"我叫{name},今年{age}岁。")
# introduce(age=15,name='小明')   #  关键字，age，name

# introduce('小明',age=16)
# introduce(age=16,'小明')   # SyntaxError: positional argument follows keyword argument

#关键字参数和位置参数  混合使用时，位置参数必须要在关键字参数之前


#默认值参数：参数有默认值，在调用的时候可以省略
#默默值参数，必须排在非默认值参数之后

# def introduce(age=18,name):   #默认值参数，排在非默认值参考前方会直接报错
# def introduce(name,age):
#     print(f"我叫{name},今年{age}岁。")
#
# # introduce('小明')
# introduce('20','小明')   #  注意逻辑上的错误


#任意多个位置参数  *args
#收集多余的位置参数为“元组”
#收集数字
#数学计算

# def sunm_all(*numbers):
#     print("收到的参数",numbers)
#     return sum(numbers)
# print(sunm_all(1,2,3,4,5))


# 任意多个关键字参数   **kwargs
##收集多余的参数为字典
# def print_user_info(**info):
#     print("用户信息")
#
#     for key,value in info.items():
#         print(f"{key}:{value}")
#
# print_user_info(name = "WC",age = 18,city = "沈阳")


#强制命名参数 *后面参数一定要以关键字参数传递出来
#在 * 或者 * args 之后的参数，必须使用关键字传入
# 提高API的可读性以及安全性，避免传参的顺序错误

# def connect(host ,*,port,timeout):
#
#     print(f"链接{host}:{port}，超时{timeout}秒")
#
# # connect("192.168.1.1",80,5)
# connect("192.168.1.1",port = 80,timeout=5)

"""
位置参数 ---默认参数 ---  * args --- **kwargs
"""


#返回值

# def add(a,b):
#     return a+b
#
# result = add(3,5)
# print(result)

# def get_profile():
#     return '张三',25,"工程师"    # python会自动将值打包成一个元组，返回的时带有三个元素的元组
# name,age,job = get_profile()    #自动解包   (张三',25,"工程师)
# """
# 在python中，只要左边的变量的数量等于右边序列（列表，元组）的元素时一致的，那么就自动的将每个元素赋值给对应的变量
# """
# # temp = (张三',25,"工程师)
# # name = temp[0]
# # age = temp[1]
# # job = temp[2]   #这个过程叫做元组解包
# print(name,age,job)


# def do_nothing():
#     return None
# print(do_nothing())




#变量的作用域  LEGB

"""
python查找变量恶的顺序
Local:(当前函数的内部)
enclosing:(外层函数，比如嵌套函数)
Global:模块级别
built-in(print len)
"""

# x = "全局变量"    # Global
# def outer():
#     x = '外层变量'    # enclosing
#     def inner():
#         x = '局部变量'
#         print('inner 打印x:',x)   # Local
#     inner()
# outer()

"""
输出局部   inner 定义   x
输出外层   inner 不定义 x
输出全局   outer & inner 中都不定义变量
"""


# 修改变量   global
#  不使用 global counter  的话，python会认为 counter +=1 是一个全新的局部的变量
# counter = 0   # 在模块的顶层定义了一个变量，它是一个全局的变量
#
# def increment():
#     global counter    #  使用 global 进行声明  我要操作的时   全局的 counter
#     counter +=1    #等价于  counter = counter +1
# increment()
# print(counter)
# UnboundLocalError: local variable 'counter' referenced before assignment


"""
在python的作用域中，如果使用   赋值  （= += -=）python会默认它就时一个局部的变量，除非我们使用 global 来进行声明

"""

# counter = 0
# def show():
#     print(counter)   #可以直接读取
# show()


# counter = 0
# def show():
#     counter +=1  # UnboundLocalError: local variable 'counter' referenced before assignment
# show()


"""
如果说，我们仅读取全局变量，我就不需要加 global
我想修改或者赋值全局变量   我们必须加上 global
"""


#修改 外层的变量   nonlocal

# def outer():
#     x = 10    # enclosing
#
#     def inner():
#         nonlocal x  #  从10 -20
#         x = 20
#         print('outer 中的 x:',x)   # Local
#     inner()
# outer()

"""
先调用的是 outer 

"""


# 查看变量   locals()

# name = '全局变量'
# def dome():
#     age = 20
#     score = 90
#     print("局部变量",locals())  # 返回一个字典  包含当前局部作用域中的所有的变量名称以及值
# # locals 查看当前函数中的局部变量，闭关返回一个字典
#     print("全局变量",list(globals().keys())[:5])
#     # globals 返回一个字典，包含当前全局的作用域，所有的变量，函数，类导入的对象以及名称
#     # globals().keys() 获取所有的全局名称
#     # list(....)[:5]  # 获取前五个
# dome()


"""
在python中数据分为可变以及不可变

不可变的： int str syuple
可变的  list   dict set
"""

# 函数不能重名
#覆盖了，容易引发，逻辑问题
# def f():
#     return '第一个'
# def f():
#     return '第二个'
# print(f())